#include "GSS-db.h"
#include "icl_graph.h"
#include "xcl2.hpp"
#include <algorithm>
#include <vector>
#include <iostream>
#include <map>
#include <stdio.h>
#include <fstream>
#include <unistd.h>
#include <time.h>
#include <set>
#include<unordered_map>
//Number of HBM Banks required
#define MAX_HBM_BANKCOUNT 4
#define BANK_NAME(n) n | XCL_MEM_TOPOLOGY
const int bank[MAX_HBM_BANKCOUNT] = {
    BANK_NAME(0),  BANK_NAME(1),  BANK_NAME(2),  BANK_NAME(3)};
const int vt_size[12] = {
    vt_size1,vt_size2,vt_size3,vt_size4,
    vt_size5,vt_size6,vt_size7,vt_size8,
    vt_size9,vt_size10,vt_size11,vt_size12};

int count_join(vector<unsigned int> &v1, vector<unsigned int> &v2)
{
      int total = 0;
      if(v1.empty()||v2.empty())
          return 0;
      unordered_map<int, int> m;
      if(v1.size()<v2.size())
      {
        for(int i=0;i<v1.size();i++)
            m[v1[i]] = 1;
         for(int i=0;i<v2.size();i++)
          {
            if(m.find(v2[i])!=m.end())
              total++;
          }
       }
       else
       {
        for(int i=0;i<v2.size();i++)
            m[v2[i]] = 1;
         for(int i=0;i<v1.size();i++)
          {
            if(m.find(v1[i])!=m.end())
              total++;
          }
       }
       return total;
}


int main(int argc, char *argv[])
{

    //TARGET_DEVICE macro needs to be passed from gcc command line
    if(argc != 2) {
		std::cout << "Usage: " << argv[0] <<" <xclbin>" << std::endl;
		return EXIT_FAILURE;
	}

    char* xclbinFilename = argv[1];
    std::cout<<argv[0]<<' '<<argv[1]<<std::endl;
    cl_int err;
    // Compute the size of array in byte
    // Creates a vector of DATA_SIZE elements with an initial value of 10 and 32
    // using customized allocator for getting buffer alignment to 4k boundary

    std::vector<cl::Device> devices;
    cl::Device device;
    std::vector<cl::Platform> platforms;
    bool found_device = false;

    //traversing all Platforms To find Xilinx Platform and targeted
    //Device in Xilinx Platform
    cl::Platform::get(&platforms);
    for(size_t i = 0; (i < platforms.size() ) & (found_device == false) ;i++){
        cl::Platform platform = platforms[i];
        std::string platformName = platform.getInfo<CL_PLATFORM_NAME>();
        if ( platformName == "Xilinx"){
            devices.clear();
            platform.getDevices(CL_DEVICE_TYPE_ACCELERATOR, &devices);
	    if (devices.size()){
		    device = devices[0];
		    found_device = true;
		    break;
	    }
        }
    }
    if (found_device == false){
       std::cout << "Error: Unable to find Target Device "
           << device.getInfo<CL_DEVICE_NAME>() << std::endl;
       return EXIT_FAILURE;
    }

    // Creating Context and Command Queue for selected device
    cl::Context context(device);
    cl::CommandQueue q(context, device, CL_QUEUE_PROFILING_ENABLE);

    // Load xclbin
    std::cout << "Loading: '" << xclbinFilename << "'\n";
    std::ifstream bin_file(xclbinFilename, std::ifstream::binary);
    bin_file.seekg (0, bin_file.end);
    unsigned nb = bin_file.tellg();
    bin_file.seekg (0, bin_file.beg);
    char *buf = new char [nb];
    bin_file.read(buf, nb);

    // Creating Program from Binary File
    cl::Program::Binaries bins;
    bins.push_back({buf,nb});
    devices.resize(1);
    cl::Program program(context, devices, bins);

	OCL_CHECK(err, cl::Kernel krnl_gss(program, "GSS", &err));
    std::cout << "begin" << std::endl;

    // std::string binaryFile = argv[1];
    // cl_int err;
    // unsigned fileBufSize;
    // Allocate Memory in Host Memory
    // When creating a buffer with user pointer (CL_MEM_USE_HOST_PTR), under the hood user ptr
    // is used if it is properly aligned. when not aligned, runtime had no choice but to create
    // its own host side buffer. So it is recommended to use this allocator if user wish to
    // create buffer using CL_MEM_USE_HOST_PTR to align user buffer to page boundary. It will
    // ensure that user buffer is used when user create Buffer/Mem object with CL_MEM_USE_HOST_PTR
    std::vector<ap_uint<B_size>, aligned_allocator<ap_uint<B_size>>> value[9];

    std::vector<unsigned int, aligned_allocator<unsigned int>> s1(benchsize);
    std::vector<unsigned int, aligned_allocator<unsigned int>> s2(benchsize);
    std::vector<int, aligned_allocator<int>> weight(benchsize);
    std::vector<unsigned int, aligned_allocator<unsigned int>> fail_s(benchsize);
    std::vector<unsigned int, aligned_allocator<unsigned int>> fail_d(benchsize);
    std::vector<int, aligned_allocator<int>> fail_w(benchsize);

    std::vector<unsigned int, aligned_allocator<unsigned int>> used(4);
    std::vector<unsigned int, aligned_allocator<unsigned int>> status(20000);
    std::vector<unsigned int, aligned_allocator<unsigned int>> neighbor(buffer_size2);

    std::fill(status.begin(),status.end(),0);
    for (int i = 0; i < 9; i++)
    {
        value[i].resize(M_size);
        std::fill(value[i].begin(),value[i].end(),0);
    }
    std::fill(s1.begin(),s1.end(),0);
    std::fill(s2.begin(),s2.end(),0);
    std::fill(weight.begin(),weight.end(),0);
    std::fill(fail_s.begin(),fail_s.end(),0);
       std::fill(fail_d.begin(),fail_d.end(),0);
       std::fill(fail_w.begin(),fail_w.end(),0);
   std::fill(status.begin(),status.end(),0);
    std::fill(neighbor.begin(),neighbor.end(),0);
   	used[0] =1;
    used[1] =1;
    used[2] =1;
    used[3] =1;
    unsigned int* full_s = new unsigned int[1000100000];
    unsigned int* full_d = new unsigned int[1000100000];
    unsigned int* full_w = new unsigned int[1000100000];
    unsigned int* check_s = new unsigned int[100000];
    unsigned int* check_d = new unsigned int[100000];
    int* check_v= new int[10000];
    unordered_map<unsigned int, int> out_degree;
    unordered_map<unsigned int, int> in_degree;
    int thre = 10;
    unsigned int s, d, val;
    set<int> vertex;
       ifstream fin("/home/haihe/data/flow.dat");
       ofstream fspeed("/home/haihe/result/speed.txt");
       ifstream fquery("/home/haihe/data/query.txt");
       ofstream fout("/home/haihe/result/query_result.txt");
       int den = 1000090542;
       for(int i=0;i<den;i++){
    	   fin>>full_s[i]>>full_d[i]>>full_w[i];
       }
       int eqs = 100000;
       int vqs = 10000;
       for(int i=0;i<eqs;i++)
       {
    	   fquery>>check_s[i]>>check_d[i];
       }
       for(int i=0;i<vqs;i++)
       {
    	   fquery>>check_v[i];
       }
       std::cout<<"file read"<<std::endl;

       std::vector<cl_mem_ext_ptr_t> valueExt(9);
         cl_mem_ext_ptr_t s1Ext,s2Ext,weightExt,neighborsExt, fsExt, fdExt, fwExt;


         for (int i = 0; i < 9; i++)
         {
             valueExt[i].obj = value[i].data();
             valueExt[i].param = 0;
             valueExt[i].flags = bank[i/3];
         }
         s1Ext.obj = s1.data();
         s1Ext.param = 0;
         s1Ext.flags = bank[3];
         s2Ext.obj = s2.data();
         s2Ext.param = 0;
         s2Ext.flags = bank[3];
         weightExt.obj = weight.data();
         weightExt.param = 0;
         weightExt.flags = bank[3];
         neighborsExt.obj = neighbor.data();
         neighborsExt.param = 0;
         neighborsExt.flags = bank[3];
         fsExt.obj = fail_s.data();
         fsExt.param = 0;
         fsExt.flags = bank[3];
         fdExt.obj = fail_d.data();
         fdExt.param = 0;
         fdExt.flags = bank[3];
         fwExt.obj = fail_w.data();
         fwExt.param = 0;
         fwExt.flags = bank[3];

    std::vector<cl::Buffer> valuebuffer(9);
    std::vector<cl::Buffer> v_tablebuffer(5);
    std::vector<cl::Buffer> rv_tablebuffer(5);
    cl::Buffer buffer_vb, buffer_rvb,buffer_s1,buffer_s2,buffer_w,buffer_used,buffer_status,buffer_neighbor, buffer_et, buffer_ret;
    cl::Buffer buffer_fs, buffer_fd, buffer_fw;
    for (int i = 0; i < 9; i++)
    {
    OCL_CHECK(err,
              valuebuffer[i] = cl::Buffer(context,
                                       CL_MEM_READ_WRITE | CL_MEM_EXT_PTR_XILINX|
                                       CL_MEM_USE_HOST_PTR,
                                       sizeof(ap_uint<B_size>) * M_size,
									   &valueExt[i],
                                       &err));
    }


    OCL_CHECK(err,
              buffer_s1 = cl::Buffer(context,
                                       CL_MEM_READ_WRITE |
									   CL_MEM_EXT_PTR_XILINX|CL_MEM_USE_HOST_PTR,
                                       sizeof(uint32_t) * benchsize,
                                       &s1Ext,
                                       &err));
    OCL_CHECK(err,
              buffer_s2 = cl::Buffer(context,
                                       CL_MEM_READ_WRITE |
									   CL_MEM_EXT_PTR_XILINX|CL_MEM_USE_HOST_PTR,
                                       sizeof(uint32_t) * benchsize,
                                       &s2Ext,
                                       &err));
    OCL_CHECK(err,
              buffer_w = cl::Buffer(context,
                                       CL_MEM_READ_WRITE |
									   CL_MEM_EXT_PTR_XILINX|CL_MEM_USE_HOST_PTR,
                                       sizeof(uint32_t) * benchsize,
                                       &weightExt,
                                       &err));
    OCL_CHECK(err,
              buffer_used = cl::Buffer(context,
                                       CL_MEM_READ_WRITE |
                                           CL_MEM_USE_HOST_PTR,
                                       sizeof(uint32_t) * 4,
                                       used.data(),
                                       &err));
    OCL_CHECK(err,
              buffer_status = cl::Buffer(context,
                                       CL_MEM_READ_WRITE |
                                           CL_MEM_USE_HOST_PTR,
                                       sizeof(uint32_t) * 20000,
                                       status.data(),
                                       &err));

	OCL_CHECK(err,
              buffer_neighbor = cl::Buffer(context,
                                       CL_MEM_READ_WRITE |
									   CL_MEM_EXT_PTR_XILINX|CL_MEM_USE_HOST_PTR,
                                       sizeof(unsigned int) * buffer_size2,
                                      &neighborsExt,
                                       &err));

	OCL_CHECK(err,
	              buffer_fs = cl::Buffer(context,
	                                       CL_MEM_READ_WRITE |
										   CL_MEM_EXT_PTR_XILINX|CL_MEM_USE_HOST_PTR,
	                                       sizeof(uint32_t) * benchsize,
	                                       &fsExt,
	                                       &err));
	    OCL_CHECK(err,
	              buffer_fd = cl::Buffer(context,
	                                       CL_MEM_READ_WRITE |
										   CL_MEM_EXT_PTR_XILINX|CL_MEM_USE_HOST_PTR,
	                                       sizeof(uint32_t) * benchsize,
	                                       &fdExt,
	                                       &err));
	    OCL_CHECK(err,
	              buffer_fw = cl::Buffer(context,
	                                       CL_MEM_READ_WRITE |
										   CL_MEM_EXT_PTR_XILINX|CL_MEM_USE_HOST_PTR,
	                                       sizeof(uint32_t) * benchsize,
	                                       &fwExt,
	                                       &err));

for (int i = 0; i < 9; i++)
{
    OCL_CHECK(err, err = krnl_gss.setArg(i, valuebuffer[i]));
}

OCL_CHECK(err, err = krnl_gss.setArg(9, buffer_s1));
OCL_CHECK(err, err = krnl_gss.setArg(10, buffer_s2));
OCL_CHECK(err, err = krnl_gss.setArg(11, buffer_w));
OCL_CHECK(err, err = krnl_gss.setArg(12, buffer_neighbor));
OCL_CHECK(err, err = krnl_gss.setArg(13, buffer_fs));
OCL_CHECK(err, err = krnl_gss.setArg(14, buffer_fd));
OCL_CHECK(err, err = krnl_gss.setArg(15, buffer_fw));
OCL_CHECK(err, err = krnl_gss.setArg(16, 0));
OCL_CHECK(err, err = krnl_gss.setArg(17, 1000000));






    std::vector<cl::Memory> inBufVec;
    std::vector<cl::Memory> outBufVec{buffer_w, buffer_neighbor};
    for (int i = 0; i < 9; i++)
    {
        inBufVec.push_back(valuebuffer[i]);
    }

    inBufVec.push_back(buffer_neighbor);
    inBufVec.push_back(buffer_fs);
    inBufVec.push_back(buffer_fd);
    inBufVec.push_back(buffer_fw);
    OCL_CHECK(err,
              err = q.enqueueMigrateMemObjects(inBufVec,
                                               0 /* 0 means from host*/));
          std::cout<<"transfer finished"<<std::endl;





vector<cl::Event> write_event(1);
vector<cl::Event> kernel_event(1);
vector<cl::Event> read_event(1);
ll_graph* lg = new ll_graph();
ll_graph* rlg = new ll_graph();
icl_graph* GB = new icl_graph();

int iter = 0;
int inserted_edges = 0;
int insert_batch = 10000000;

double total_time = 0;

for(int batch_iter=0;batch_iter<100;batch_iter++)
{
iter = 0;
auto t1 = std::chrono::system_clock::now();
for(int p=0;p<insert_batch;p++){
s = full_s[batch_iter*insert_batch + p];
d = full_d[batch_iter*insert_batch + p];
val = full_w[batch_iter*insert_batch + p];


bool matrix_insert=false;
if(out_degree[s] >= thre || in_degree[d] >= thre)
	matrix_insert = true;


if(out_degree[s]<thre)
{
	int increment = lg->insert(s, d, val);
	if(increment!=0){
	unordered_map<unsigned int, int>::iterator it = out_degree.find(s);
	if(it!=out_degree.end())
		it->second += increment;
	else
		out_degree[s] = increment;

	if(out_degree[s]==thre)
	{
		map<unsigned int, int> me;
		lg->delete_node(s, me);
		map<unsigned int, int>::iterator it;
		for(it = me.begin();it!=me.end();it++)
		{
			if(in_degree[it->first]<thre){
			unsigned int v;
			v = it->first;

			s1[iter] = s;
			s2[iter] = v;
			weight[iter] = it->second;

			fail_s[iter] = 0;
			fail_d[iter] = 0;
			fail_w[iter] = 0;
			iter++;
			}
		}
		me.clear();
	}
  }
}

if(in_degree[d]<thre){
		int increment = rlg->insert(d, s, val);
		if(increment!=0){
		unordered_map<unsigned int, int>::iterator it = in_degree.find(d);
		if(it!=in_degree.end())
			it->second += increment;
		else
			in_degree[d] = increment;

		if(in_degree[d]==thre)
		{
			map<unsigned int, int> me;
			rlg->delete_node(d, me);
			map<unsigned int, int>::iterator it;
			for(it = me.begin();it!=me.end();it++)
			{
				if(out_degree[it->first]<thre){ // only if the out degree of the source node is not above the threshold, we add it to GSS, otherwise the edge has already been inserted to GSS.

				unsigned int v;
				v = it->first;

				s1[iter]= v;
				s2[iter] = d;
				weight[iter] = it->second;
				fail_s[iter] = 0;
				fail_d[iter] = 0;
				fail_w[iter] = 0;
				iter++;
				}
			}
			me.clear();
		}
	  }
}
if(matrix_insert)
{

	s1[iter] = s;
	s2[iter] = d;
	weight[iter] = val;
	 fail_w[iter] = 0;
	 fail_s[iter] = 0;
	 fail_d[iter] = 0;
	 iter++;
}
	 if(iter>=999900)
	 {
		 inserted_edges += iter;
		 for(;iter<=1000000;iter++)
		 {
			 s1[iter] = 0;
			 s2[iter] = 0;
			 weight[iter] = 0;
			 fail_w[iter] = 0;
			 fail_s[iter] = 0;
			 fail_d[iter] = 0;
		 }
		 OCL_CHECK(err, err = krnl_gss.setArg(16, 0));
		 OCL_CHECK(err, err = krnl_gss.setArg(17, benchsize));
		 OCL_CHECK(err, err = q.enqueueMigrateMemObjects({buffer_s1, buffer_s2, buffer_w, buffer_fs, buffer_fd, buffer_fw},
		                                                0,  NULL, &write_event[0]));


		 OCL_CHECK(err, err = cl::Event::waitForEvents(write_event));

	 q.enqueueTask(krnl_gss,NULL, &kernel_event[0]);

	  OCL_CHECK(err, err = cl::Event::waitForEvents(kernel_event));

	  OCL_CHECK(err, err = q.enqueueMigrateMemObjects({buffer_fs, buffer_fd, buffer_fw},
	                  CL_MIGRATE_MEM_OBJECT_HOST, NULL, &read_event[0]));
	 OCL_CHECK(err, err = cl::Event::waitForEvents(read_event));
	 for(int j=0;j<benchsize;j++)
	{
	 	if(fail_w[j]>0)
	 	{
	 	     GB->update(fail_s[j], fail_d[j], fail_w[j]);
	 	}
	 }
	        iter = 0;
	 }
}
//inserted_edges += iter;
if(iter>0)
{
for(;iter<=1000000;iter++)
{
			 s1[iter] = 0;
			 s2[iter] = 0;
			 weight[iter] = 0;
			 fail_w[iter] = 0;
			 fail_s[iter] = 0;
			 fail_d[iter] = 0;
}
OCL_CHECK(err, err = krnl_gss.setArg(16, 0));
OCL_CHECK(err, err = krnl_gss.setArg(17, benchsize));
		 OCL_CHECK(err, err = q.enqueueMigrateMemObjects({buffer_s1, buffer_s2, buffer_w, buffer_fs, buffer_fd, buffer_fw},
		                                                0,  NULL, &write_event[0]));


		 OCL_CHECK(err, err = cl::Event::waitForEvents(write_event));

	 q.enqueueTask(krnl_gss,NULL, &kernel_event[0]);

	  OCL_CHECK(err, err = cl::Event::waitForEvents(kernel_event));

	  OCL_CHECK(err, err = q.enqueueMigrateMemObjects({buffer_fs, buffer_fd, buffer_fw},
	                  CL_MIGRATE_MEM_OBJECT_HOST, NULL, &read_event[0]));
	 OCL_CHECK(err, err = cl::Event::waitForEvents(read_event));
	         for(int j=0;j<benchsize;j++)
	         {
	        	 if(fail_w[j]>0)
	        	 {
	        		 GB->update(fail_s[j], fail_d[j], fail_w[j]);
	        	 }
	         }
}

fout<<"batch "<<batch_iter<<": "<<endl;



// edge query
OCL_CHECK(err, err = krnl_gss.setArg(16, 1));
OCL_CHECK(err, err = krnl_gss.setArg(17, eqs));
for(int j=0;j<eqs;j++)
{
	s1[j]= check_s[j];
	s2[j] = check_d[j];
}
OCL_CHECK(err, err = q.enqueueMigrateMemObjects({buffer_s1, buffer_s2},
                                    0,  NULL, &write_event[0]));
OCL_CHECK(err, err = cl::Event::waitForEvents(write_event));
        q.enqueueTask(krnl_gss,NULL, &kernel_event[0]);

    OCL_CHECK(err, err = cl::Event::waitForEvents(kernel_event));
     OCL_CHECK(err, err = q.enqueueMigrateMemObjects({buffer_w},
              CL_MIGRATE_MEM_OBJECT_HOST, NULL, &read_event[0]));
      OCL_CHECK(err, err = cl::Event::waitForEvents(read_event));
      for(int j=0;j<eqs;j++)
      {
    	  if(weight[j]==0)
    	  {
    		  weight[j]=GB->query(s1[j], s2[j]);
    	  }
    	  if(weight[j]==0){
    		  weight[j] = lg->query(s1[j], s2[j]);
    	  }

    	 fout<<check_s[j]<<' '<<check_d[j]<<' '<<weight[j]<<endl;
      }


// 1-hop successor query

      OCL_CHECK(err, err = krnl_gss.setArg(16, 2));

      unordered_map<unsigned int, vector<unsigned int> > sucs;
      int count =0;
    for(int j=0;j<vqs;j++){
    	int qv = check_v[j];
  	sucs[qv].clear();
  	if(out_degree[qv]>=thre)
  	{
  		GB->successor(qv, sucs[qv]);
  		s1[count] = qv;
  		count++;
  	}
  	else
  	{
  		lg->successor(qv, sucs[qv]);
  	}
  }

  if(count>0){
  	OCL_CHECK(err, err = krnl_gss.setArg(17, count));
  	 OCL_CHECK(err, err = q.enqueueMigrateMemObjects({buffer_s1},
  	                                                0, NULL, &write_event[0]));

  	 OCL_CHECK(err, err = cl::Event::waitForEvents(write_event));

  	 q.enqueueTask(krnl_gss,NULL, &kernel_event[0]);

  	    OCL_CHECK(err, err = cl::Event::waitForEvents(kernel_event));
  	OCL_CHECK(err, err = q.enqueueMigrateMemObjects({buffer_neighbor},
                CL_MIGRATE_MEM_OBJECT_HOST, NULL, &read_event[0]));
         OCL_CHECK(err, err = cl::Event::waitForEvents(read_event));

         int index = 0;
         for(int j=0;j<count;j++)
         {
             while(neighbor[index]!=0)
           {
              sucs[s1[j]].push_back(neighbor[index]);
              index++;
           }
              index++;
          }
  }

   	   unordered_map<unsigned int, vector<unsigned int> >::iterator mit;
      for(mit = sucs.begin();mit!=sucs.end();mit++)
      {
      	fout<<mit->first<<endl;
      	sort(mit->second.begin(), mit->second.end());
      	for(int j=0;j<mit->second.size();j++)
      	{
      		fout<<mit->second[j]<<' ';
      	}
      	fout<<-1<<endl;
      	mit->second.clear();
      }
      sucs.clear();


// precursor query

      OCL_CHECK(err, err = krnl_gss.setArg(16, 3));

         unordered_map<unsigned int, vector<unsigned int> > pres;
         count =0;
         for(int j=0;j<vqs;j++){
         	int qv = check_v[j];
       	pres[qv].clear();
       	if(in_degree[qv]>=thre)
       	{
       		GB->precursor(qv, pres[qv]);
       		s1[count] = qv;
       		count++;
       	}
       	else
       	{
       		rlg->successor(qv, pres[qv]);
       	}
       }

       if(count>0){
       	OCL_CHECK(err, err = krnl_gss.setArg(17, count));
       	 OCL_CHECK(err, err = q.enqueueMigrateMemObjects({buffer_s1},
       	                                                0, NULL, &write_event[0]));

       	 OCL_CHECK(err, err = cl::Event::waitForEvents(write_event));

       	 q.enqueueTask(krnl_gss,NULL, &kernel_event[0]);

       	    OCL_CHECK(err, err = cl::Event::waitForEvents(kernel_event));
       	OCL_CHECK(err, err = q.enqueueMigrateMemObjects({buffer_neighbor},
                     CL_MIGRATE_MEM_OBJECT_HOST, NULL, &read_event[0]));
              OCL_CHECK(err, err = cl::Event::waitForEvents(read_event));

              int index = 0;
              for(int j=0;j<count;j++)
              {
                  while(neighbor[index]!=0)
                {
                   pres[s1[j]].push_back(neighbor[index]);
                   index++;
                }
                   index++;
               }
       }

           for(mit = pres.begin();mit!=pres.end();mit++)
           {
           	fout<<mit->first<<endl;
           	sort(mit->second.begin(), mit->second.end());
           	for(int j=0;j<mit->second.size();j++)
           	{
           		fout<<mit->second[j]<<' ';
           	}
           	fout<<-1<<endl;
           	mit->second.clear();
           }
           pres.clear();


// 3-hop circle detection
 for(int j=0;j<eqs;j++)
 {
 	unsigned int s = check_s[j];
 	unsigned int d = check_d[j];
 	sucs[d].clear();
 	pres[s].clear();
 }

OCL_CHECK(err, err = krnl_gss.setArg(16, 2));
count = 0;
for(mit = sucs.begin();mit!=sucs.end();mit++)
{
  int qv = mit->first;
  if(out_degree[qv]>=thre)
  {
      GB->successor(qv, sucs[qv]);
      s1[count] = qv;
      count++;
  }
  else
    lg->successor(qv, sucs[qv]);
}

if(count>0)
  {
      OCL_CHECK(err, err = krnl_gss.setArg(17, count));
     OCL_CHECK(err, err = q.enqueueMigrateMemObjects({buffer_s1},
                                                    0, NULL, &write_event[0]));

     OCL_CHECK(err, err = cl::Event::waitForEvents(write_event));

     q.enqueueTask(krnl_gss,NULL, &kernel_event[0]);

        OCL_CHECK(err, err = cl::Event::waitForEvents(kernel_event));
    OCL_CHECK(err, err = q.enqueueMigrateMemObjects({buffer_neighbor},
                CL_MIGRATE_MEM_OBJECT_HOST, NULL, &read_event[0]));
         OCL_CHECK(err, err = cl::Event::waitForEvents(read_event));

         int index = 0;
         for(int j=0;j<count;j++)
         {
             while(neighbor[index]!=0)
           {
              sucs[s1[j]].push_back(neighbor[index]);
              index++;
           }
              index++;
          }
          count = 0;
  }

OCL_CHECK(err, err = krnl_gss.setArg(16, 3));
count = 0;
for(mit = pres.begin();mit!=pres.end();mit++)
{
  int qv = mit->first;
  pres[qv].clear();
  if(in_degree[qv]>=thre)
{
  GB->precursor(qv, pres[qv]);
  s1[count] = qv;
  count++;
}
else
  rlg->successor(qv, pres[qv]);
}

if(count>0)
  {
     OCL_CHECK(err, err = krnl_gss.setArg(17, count));
     OCL_CHECK(err, err = q.enqueueMigrateMemObjects({buffer_s1},
                                                    0, NULL, &write_event[0]));

     OCL_CHECK(err, err = cl::Event::waitForEvents(write_event));

     q.enqueueTask(krnl_gss,NULL, &kernel_event[0]);

        OCL_CHECK(err, err = cl::Event::waitForEvents(kernel_event));
    OCL_CHECK(err, err = q.enqueueMigrateMemObjects({buffer_neighbor},
                CL_MIGRATE_MEM_OBJECT_HOST, NULL, &read_event[0]));
         OCL_CHECK(err, err = cl::Event::waitForEvents(read_event));

         int index = 0;
         for(int j=0;j<count;j++)
         {
             while(neighbor[index]!=0)
           {
              pres[s1[j]].push_back(neighbor[index]);
              index++;
           }
              index++;
          }
          count = 0;
  }

  unordered_map<unsigned long long, int> tri;
  for(int j=0;j<eqs;j++)
  {
    int s = check_s[j];
    int d = check_d[j];
    fout<<s<<' '<<d<<' ';
    unsigned long long p = ((((unsigned long long)s)<<32)|((unsigned long long)d));
    unordered_map<unsigned long long, int>::iterator it = tri.find(p);
    if(it!=tri.end())
      fout<<it->second<<endl;
    else
    {
      int tmp = count_join(sucs[d], pres[s]);
      fout<<tmp<<endl;
      tri[p] =tmp;
    }
  }
  tri.clear();
  pres.clear();
  sucs.clear();

auto t2 = std::chrono::system_clock::now();

double second = std::chrono::duration<double, std::milli>(t2-t1).count()/1000;
double speed = insert_batch/second;
total_time += second;
fspeed<<"Batch "<<batch_iter<<": "<<speed<<endl;
cout<<"batch "<<batch_iter<<" processed"<<endl;
cout<<"speed: "<<speed<<endl;
cout<<endl;
}

auto t1 = std::chrono::system_clock::now();
iter = 0;
for(int p=0;p<90542;p++)
{
	s = full_s[100*insert_batch + p];
	d = full_d[100*insert_batch + p];
	val = full_w[100*insert_batch + p];


	bool matrix_insert=false;
	if(out_degree[s] >= thre || in_degree[d] >= thre)
		matrix_insert = true;


	if(out_degree[s]<thre)
	{
		int increment = lg->insert(s, d, val);
		if(increment!=0){
		unordered_map<unsigned int, int>::iterator it = out_degree.find(s);
		if(it!=out_degree.end())
			it->second += increment;
		else
			out_degree[s] = increment;

		if(out_degree[s]==thre)
		{
			map<unsigned int, int> me;
			lg->delete_node(s, me);
			map<unsigned int, int>::iterator it;
			for(it = me.begin();it!=me.end();it++)
			{
				if(in_degree[it->first]<thre){
				unsigned int v;
				v = it->first;

				s1[iter] = s;
				s2[iter] = v;
				weight[iter] = it->second;

				fail_s[iter] = 0;
				fail_d[iter] = 0;
				fail_w[iter] = 0;
				iter++;
				}
			}
			me.clear();
		}
	  }
	}

	if(in_degree[d]<thre){
			int increment = rlg->insert(d, s, val);
			if(increment!=0){
			unordered_map<unsigned int, int>::iterator it = in_degree.find(d);
			if(it!=in_degree.end())
				it->second += increment;
			else
				in_degree[d] = increment;

			if(in_degree[d]==thre)
			{
				map<unsigned int, int> me;
				rlg->delete_node(d, me);
				map<unsigned int, int>::iterator it;
				for(it = me.begin();it!=me.end();it++)
				{
					if(out_degree[it->first]<thre){ // only if the out degree of the source node is not above the threshold, we add it to GSS, otherwise the edge has already been inserted to GSS.

					unsigned int v;
					v = it->first;

					s1[iter]= v;
					s2[iter] = d;
					weight[iter] = it->second;
					fail_s[iter] = 0;
					fail_d[iter] = 0;
					fail_w[iter] = 0;
					iter++;
					}
				}
				me.clear();
			}
		  }
	}
	if(matrix_insert)
	{

		s1[iter] = s;
		s2[iter] = d;
		weight[iter] = val;
		 fail_w[iter] = 0;
		 fail_s[iter] = 0;
		 fail_d[iter] = 0;
		 iter++;
	}
}

if(iter>0)
{
for(;iter<=100000;iter++)
{
	s1[iter] = 0;
	s2[iter] = 0;
	weight[iter] = 0;
	fail_w[iter] = 0;
	fail_s[iter] = 0;
	fail_d[iter] = 0;
}
OCL_CHECK(err, err = krnl_gss.setArg(16, 0));
OCL_CHECK(err, err = krnl_gss.setArg(17, 100000));
		 OCL_CHECK(err, err = q.enqueueMigrateMemObjects({buffer_s1, buffer_s2, buffer_w, buffer_fs, buffer_fd, buffer_fw},
		                                                0,  NULL, &write_event[0]));


		 OCL_CHECK(err, err = cl::Event::waitForEvents(write_event));

	 q.enqueueTask(krnl_gss,NULL, &kernel_event[0]);

	  OCL_CHECK(err, err = cl::Event::waitForEvents(kernel_event));

	  OCL_CHECK(err, err = q.enqueueMigrateMemObjects({buffer_fs, buffer_fd, buffer_fw},
	                  CL_MIGRATE_MEM_OBJECT_HOST, NULL, &read_event[0]));
	 OCL_CHECK(err, err = cl::Event::waitForEvents(read_event));
	         for(int j=0;j<100000;j++)
	         {
	        	 if(fail_w[j]>0)
	        	 {
	        		 GB->update(fail_s[j], fail_d[j], fail_w[j]);
	        	 }
	         }
}


auto t2 = std::chrono::system_clock::now();

double second = std::chrono::duration<double, std::milli>(t2-t1).count()/1000;
total_time += second;

//cout<<inserted_edges<<endl;
fspeed<<"Average: "<<((double)(den)/total_time)<<endl;
cout<<"Average speed: "<<((double)(den)/total_time)<<endl;

q.finish();
delete []full_s;
delete []full_d;
delete lg;
delete rlg;
out_degree.clear();
in_degree.clear();
        std::cout << "TEST FINISHED" << std::endl;
        return  EXIT_SUCCESS;
}
